package com.dctp.ems.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dctp.common.cache.ContractCache;
import com.dctp.common.cache.ProfitStatCache;
import com.dctp.common.cache.UserCache;
import com.dctp.common.controller.BaseController;
import com.dctp.common.core.RedisManager;
import com.dctp.common.db.UserDb;
import com.dctp.common.service.TradeService;
import com.dctp.common.utils.CollectUtil;
import com.dctp.common.utils.DateUtil;
import com.dctp.common.utils.NumberUtil;
import com.dctp.common.utils.StringUtil;
import com.dctp.common.vo.*;
import com.jfinal.aop.Before;
import com.jfinal.core.NotAction;
import com.jfinal.core.Path;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.tx.Tx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Path("/compensate")
public class CompensateController extends BaseController {

    private static ExecutorService es = Executors.newFixedThreadPool(1);
    private static final Logger logger = LoggerFactory.getLogger(CompensateController.class);
    private Integer getCompensateQuantity(String symbol,Long userId) {

        BigDecimal initEquity = BigDecimal.ZERO;
        String initEquityStr = RedisManager.getInstance().get(String.format(RedisKey.USER_INIT_PROFIT, userId));
        if (StringUtil.isNotEmpty(initEquityStr)) {
            initEquity = NumberUtil.parseBigDecimal(initEquityStr);
        }else {
            ProfitStat profitStat = ProfitStatCache.get(userId);
            if (null != profitStat) {
                initEquity = profitStat.getInitEquity();
            }
        }
        Record config = Db.findFirst(
                "SELECT id, down_limit, up_limit, quantity " +
                        "FROM dctp_compensate_config " +
                        "WHERE symbol = ? " +
                        "AND ? >= down_limit " +
                        "AND ? < up_limit " +
                        "ORDER BY down_limit DESC " +
                        "LIMIT 1",
                symbol, initEquity, initEquity
        );

        if (config != null) {
            return config.getInt("quantity");
        } else {
            return 0;
        }
    }

    @SuppressWarnings("unchecked")
    private void handleSingle(Long userId, String symbol,Integer direction,BigDecimal compensateAmount) {

        Integer count = Db.queryInt("select count(*) from dctp_order where user_id = ? and symbol = ? and direction = ? and compensate = 0  ", userId,symbol,direction);
        if (null == count || count == 0) {
            throw new RuntimeException("该用户没有可补仓的订单");
        }

        Integer quantity = getCompensateQuantity(symbol, userId);
        if (null == quantity || quantity == 0) {
            throw new RuntimeException("改用户可补手数为0");
        }

        Map<String,Object> whereAndParams = getWhereAndParams(true);
        List<Object> params = (List<Object>) whereAndParams.get("params");
        StringBuilder where = (StringBuilder) whereAndParams.get("where");
        where.append(" and user_id = ? and symbol = ? and direction = ? ");
        params.add(userId);
        params.add(symbol);
        params.add(direction);
        int r = Db.update("UPDATE dctp_order SET compensate = 1, update_time = now() " + where,params.toArray());
        if (r == 0 ) {
            throw new RuntimeException("Failed to update order compensate status for order");
        }

        // 检查用户是否存在
        Record user = UserDb.findBasicById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        BigDecimal amount = null;
        String key = "";
        if (null == compensateAmount) {
            key = String.format(RedisKey.USER_COMPENSATE_AMOUNT,userId,symbol,direction);
            String totalCompensateAmount = RedisManager.getInstance().get(key);
            if (StringUtil.isEmpty(totalCompensateAmount)) {
                throw new RuntimeException("补仓失败，不符合补仓条件");
            }
            // 更新用户余额
            amount = NumberUtil.parseBigDecimal(totalCompensateAmount);
            amount = amount.abs();//转为正数
        }else {
            amount = compensateAmount;
        }

        int ur = Db.update("UPDATE dctp_user SET balance = balance + ? WHERE id = ? ", amount, userId);
        if (ur != 1) {
            throw new RuntimeException("Failed to update user balance for user: " + userId);
        }
        // 记录赔付记录
        BigDecimal balance = user.getBigDecimal("balance");

        //写入资金明细
        TradeService.saveFundFlow(user, FundChangeType.COMEPENSATE,balance,balance.add(amount),amount,userId,"补仓");

        //删除redis
        if (StringUtil.isNotEmpty(key)) {
            RedisManager.getInstance().del(key);
        }
    }

    @Before(Tx.class)
    public void compensate() {
        Record body = jsonBody();
        logger.info("body:{}", body.toJson());
        String symbol = body.getStr("symbol");
        if (StringUtil.isEmpty(symbol)) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }
        Long userId = body.getLong("user_id");
        if (null == userId) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        Integer direction = body.getInt("direction");
        if (null == direction) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        String amount = body.getStr("total_compensate_amount");
        BigDecimal compensateAmount = null;
        if (StringUtil.isNotEmpty(amount)) {
            compensateAmount = new BigDecimal(amount);
        }
        this.handleSingle(userId,symbol,direction,compensateAmount);
        renderJson(JsonResult.success());
    }


    /***
     * 批量补仓
     */
    @Before(Tx.class)
    public void batchCompensate() {

        Record body = jsonBody();
        String userData = body.getStr("userData");
        if (StringUtil.isEmpty(userData)) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        JSONArray users = JSONArray.parseArray(userData);
        if (null == users || users.isEmpty()) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        Integer direction = body.getInt("direction");
        if (null == direction) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        for (Object user : users) {
            JSONObject obj = (JSONObject) user;
            this.handleSingle(
                    obj.getLong("user_id"),
                    obj.getString("symbol"),
                    direction,null);
        }
        renderJson(JsonResult.success());
    }


    @SuppressWarnings("unchecked")
    public void allCompensate() {

        Map<String,Object> whereAndParams = getWhereAndParams(false);
        List<Object> params = (List<Object>) whereAndParams.get("params");
        StringBuilder where = (StringBuilder) whereAndParams.get("where");

        // 构造最终 SQL
        String select = "SELECT " +
                "SUM(realized_pnl) AS total_pnl, " +
                "SUM(quantity) AS total_quantity, " +
                "SUM(open_service_fee + close_service_fee) AS total_service_fee, " +
                "SUM(margin_occupied) AS total_margin_occupied, " +
                "symbol, user_id,compensate ";

        String from = "FROM dctp_order " + where + " GROUP BY user_id, symbol HAVING SUM(realized_pnl) < 0";
        List<Record> list = Db.find(select + from, params.toArray());

        Integer direction = getParaToInt("direction");
        if (null == direction) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        Integer others = getParaToInt("others",0);
        if (CollectUtil.isNotEmpty(list)) {
            es.execute(() -> {
                for (Record r : list) {
                    Db.tx(()-> {
                        try {

                            Long uid = r.getLong("user_id");
                            String symbol = r.getStr("symbol");
                            List<Map<String,Object>> timeWhereList = getTimeWhereAndParams(false);
                            BigDecimal totalCompensateAmount = BigDecimal.ZERO;
                            for (Map<String, Object> item : timeWhereList) {

                                StringBuilder whereSql = (StringBuilder) item.get("where");
                                List<Object> paramList = (List<Object>) item.get("params");
                                whereSql.append(" and user_id = ? and symbol = ? and direction = ? ");
                                paramList.add(uid);
                                paramList.add(symbol);
                                paramList.add(direction);

                                //统计每手平均亏损
                                String sql  = " SELECT *  FROM dctp_order " + whereSql;
                                if (others == 1) {
                                    //其他补仓，只统计亏损订单
                                    sql += " and realized_pnl < 0 ";
                                }

                                List<Record> userOrderList = Db.find(sql, paramList.toArray());

                                //可补仓手数
                                Integer compensateQuantity = getCompensateQuantity(symbol,uid);
                                BigDecimal activityCompensateAmount = BigDecimal.ZERO;
                                if (CollectUtil.isNotEmpty(userOrderList)) {
                                    int ljCount = 0;
                                    outer:
                                    for (Record order : userOrderList) {
                                        BigDecimal realizedPnl = order.getBigDecimal("realized_pnl");
                                        int quantity = order.getInt("quantity");

                                        // 每手平均盈利或者亏损
                                        BigDecimal avg = realizedPnl.divide(BigDecimal.valueOf(quantity), 2, RoundingMode.HALF_UP);

                                        if (quantity >= compensateQuantity) {
                                            activityCompensateAmount = BigDecimal.valueOf(compensateQuantity).multiply(avg);
                                            break; // 直接跳出 outer 循环
                                        } else {
                                            for (int i = 0; i < quantity; i++) {
                                                if (ljCount < compensateQuantity) {
                                                    activityCompensateAmount = activityCompensateAmount.add(avg);
                                                    ljCount += 1;
                                                } else {
                                                    break outer; // ⚠️ 直接跳出外层循环
                                                }
                                            }
                                        }
                                    }
                                    //总盈亏
                                    totalCompensateAmount = totalCompensateAmount.add(activityCompensateAmount);
                                }
                            }
                            totalCompensateAmount = totalCompensateAmount.setScale(2,RoundingMode.HALF_UP);
                            if (totalCompensateAmount.compareTo(BigDecimal.ZERO) < 0) {
                                String key = String.format(RedisKey.USER_COMPENSATE_AMOUNT,uid,symbol,direction);
                                RedisManager.getInstance().set(key, totalCompensateAmount.toPlainString());
                            }
                            this.handleSingle(uid,symbol,direction,null);
                            return true;
                        }catch (Exception e) {
                            logger.error(e.getMessage(),e);
                            return false;
                        }
                    });
                }
            });
        }

        renderJson(JsonResult.success());
    }


    @Before(Tx.class)
    public void customCompensate() {

        Record body = jsonBody();
        Long userId = body.getLong("user_id");
        if (null == userId) {
            renderJson(JsonResult.failWithMessage("参数错误"));
            return;
        }

        String amount = body.getStr("custom_amount");
        if (StringUtil.isEmpty(amount)) {
            renderJson(JsonResult.failWithMessage("参数错误"));
            return;
        }

        BigDecimal customAmount = NumberUtil.parseBigDecimal(amount);
        if (null == customAmount || customAmount.compareTo(BigDecimal.ZERO) <= 0) {
            renderJson(JsonResult.failWithMessage("参数错误"));
            return;
        }

        Record user = Db.findById("dctp_user",userId);
        if (null == user) {
            renderJson(JsonResult.failWithMessage("该用户不存在"));
            return;
        }

        if (user.getInt("status") == 0) {
            renderJson(JsonResult.failWithMessage("用户状态异常"));
            return;
        }

        int ur = Db.update("UPDATE dctp_user SET balance = balance + ? WHERE id = ? ", customAmount, userId);
        if (ur != 1) {
            throw new RuntimeException("自定义补仓失败");
        }
        // 记录赔付记录
        BigDecimal balance = user.getBigDecimal("balance");

        //写入资金明细
        TradeService.saveFundFlow(user, FundChangeType.COMEPENSATE,balance,balance.add(customAmount),customAmount,userId,"自定义补仓");

        renderJson(JsonResult.success());
    }


    @SuppressWarnings("unchecked")
    public void stat() {

        int page = getInt("currentPage", 1);
        int pageSize = getInt("pageSize", 10);

        Map<String,Object> whereAndParams = getWhereAndParams(false);
        List<Object> params = (List<Object>) whereAndParams.get("params");
        StringBuilder where = (StringBuilder) whereAndParams.get("where");

        // 构造最终 SQL
        String select = "SELECT " +
                "SUM(realized_pnl) AS total_pnl, " +
                "SUM(quantity) AS total_quantity, " +
                "SUM(open_service_fee + close_service_fee) AS total_service_fee, " +
                "SUM(margin_occupied) AS total_margin_occupied, " +
                "symbol, user_id,compensate ";

        String from  = "FROM dctp_order " + where + " GROUP BY user_id, symbol ";

        Integer loss = getParaToInt("loss");
        if (null != loss) {
            from += " HAVING SUM(realized_pnl) " + (loss == 1 ? "<" : ">") + " 0";
        }

        Integer direction = getParaToInt("direction");
        Integer others = getParaToInt("others",0);
        var pageData = Db.paginate(page,pageSize,true,select,from,params.toArray());
        if (CollectUtil.isNotEmpty(pageData.getList())) {

            for (Record r : pageData.getList()) {
                Long uid = r.getLong("user_id");
                String symbol = r.getStr("symbol");

                List<Map<String,Object>> timeWhereList = getTimeWhereAndParams(false);

                BigDecimal totalCompensateAmount = BigDecimal.ZERO;
                //最大可补
                int maxQuantity = 0;
                //实际可补
                int actualQuantity = 0;
                for (Map<String, Object> item : timeWhereList) {
                    StringBuilder whereSql = (StringBuilder) item.get("where");
                    List<Object> paramList = (List<Object>) item.get("params");
                    whereSql.append(" and user_id = ? and symbol = ? and direction = ? ");
                    paramList.add(uid);
                    paramList.add(symbol);
                    paramList.add(direction);

                    //统计每手平均亏损
                    String sql  = " SELECT *  FROM dctp_order " + whereSql;
                    if (others == 1) {
                        //其他补仓，只统计亏损订单
                        sql += " and realized_pnl < 0 ";
                    }

                    List<Record> userOrderList = Db.find(sql, paramList.toArray());
                    //可补仓手数
                    Integer compensateQuantity = getCompensateQuantity(symbol,uid);
                    BigDecimal activityCompensateAmount = BigDecimal.ZERO;
                    if (CollectUtil.isNotEmpty(userOrderList)) {
                        int ljCount = 0;
                        maxQuantity += compensateQuantity;
                        outer:
                        for (Record order : userOrderList) {
                            BigDecimal realizedPnl = order.getBigDecimal("realized_pnl");
                            int quantity = order.getInt("quantity");

                            // 每手平均盈利或者亏损
                            BigDecimal avg = realizedPnl.divide(BigDecimal.valueOf(quantity), 2, RoundingMode.HALF_UP);

                            if (quantity >= compensateQuantity && ljCount == 0) {
                                activityCompensateAmount = BigDecimal.valueOf(compensateQuantity).multiply(avg);
                                if (avg.compareTo(BigDecimal.ZERO) < 0) {
                                    actualQuantity += compensateQuantity;
                                }
                                break; // 直接跳出 outer 循环
                            } else {
                                for (int i = 0; i < quantity; i++) {
                                    if (ljCount < compensateQuantity) {
                                        activityCompensateAmount = activityCompensateAmount.add(avg);
                                        ljCount += 1;
                                        if (avg.compareTo(BigDecimal.ZERO) < 0) {
                                            actualQuantity += 1;
                                        }
                                    } else {
                                        break outer; // ⚠️ 直接跳出外层循环
                                    }
                                }
                            }
                        }
                        //总盈亏
                        totalCompensateAmount = totalCompensateAmount.add(activityCompensateAmount);
                    }
                }
                totalCompensateAmount = totalCompensateAmount.setScale(2,RoundingMode.HALF_UP);

                if (totalCompensateAmount.compareTo(BigDecimal.ZERO) < 0) {
                    String key = String.format(RedisKey.USER_COMPENSATE_AMOUNT,uid,symbol,direction);
                    RedisManager.getInstance().set(key, totalCompensateAmount.toPlainString());
                }

                r.set("total_compensate_amount",totalCompensateAmount);

                r.set("max_quantity",maxQuantity);
                r.set("actual_quantity",actualQuantity);

                ProfitStat profitStat = ProfitStatCache.get(uid);
                if (null != profitStat) {
                    r.set("init_equity",profitStat.getInitEquity());
                }

                BigDecimal avgCompensate = BigDecimal.ZERO;
                if (actualQuantity > 0) {
                    avgCompensate = totalCompensateAmount.divide(BigDecimal.valueOf(actualQuantity),2,RoundingMode.HALF_UP);
                }
                r.set("avg_compensate",avgCompensate);
                r.set("symbol_name", ContractCache.getNameBySymbol(r.getStr("symbol")));
                Record user = UserCache.getBasic(uid);
                if (null != user) {
                    r.set("user",UserCache.getShowInfo(uid));
                    String key = getGroupByIdKey();
                    Long orgId = user.getLong(key);
                    if (null != orgId) {
                        Record showInfo = UserCache.getShowInfo(orgId);
                        r.set("orgInfo", showInfo);
                    }
                }
            }
        }
        renderJson(JsonResult.successWithData(
                new PageResult<>(pageData.getList(), pageData.getTotalRow(), pageSize, page)
        ));
    }

    @NotAction
    private Map<String,Object> getWhereAndParams(boolean isCompensate) {

        StringBuilder where = new StringBuilder(" WHERE status = 2 ");
        List<Object> params = new ArrayList<>();

        Record body = jsonBody();

        if (!isCompensate) {
            // symbol 筛选
            String symbol = getPara("symbol");
            if (StringUtil.isNotEmpty(symbol)) {
                where.append(" AND symbol = ?");
                params.add(symbol);
            }

            String userId = getPara("user_id");
            if (StringUtil.isNotEmpty(userId)) {
                where.append(" AND user_id = ?");
                params.add(userId);
            }

            Integer direction = getParaToInt("direction");
            if (null != direction) {
                where.append(" AND direction = ?");
                params.add(direction);
            }
        }


        // 平仓时间筛选
        String closeTime = "";
        if (isCompensate) {
            closeTime = body.getStr("close_time");
        }else {
            closeTime = getPara("close_time");
        }
        if (StringUtil.isNotEmpty(closeTime)) {
            String closeBeginTime = closeTime.split("@")[0];
            String closeEndTime = closeTime.split("@")[1];

            if (StringUtil.isNotEmpty(closeBeginTime)) {
                where.append(" AND close_time >= ?");
                params.add(DateUtil.parse(closeBeginTime, "yyyy-MM-dd HH:mm:ss"));
            }
            if (StringUtil.isNotEmpty(closeEndTime)) {
                where.append(" AND close_time <= ?");
                params.add(DateUtil.parse(closeEndTime, "yyyy-MM-dd HH:mm:ss"));
            }
        }


        // 活动时间段 openTimeList 生成（用于判断开仓时间是否在范围内）
        Integer compensate = null;
        if (isCompensate) {
            compensate = body.getInt("compensate");
        }else {
            compensate = getParaToInt("compensate");
        }
        if (null != compensate) {
            where.append(" AND compensate = ?");
            params.add(compensate);
        }

        String roleKey = "";
        String roleUserId = null;
        if (isCompensate) {
            roleKey = body.getStr("roleKey");
            roleUserId = body.getStr("roleUserId");
        }else {
            roleKey = getPara("roleKey");
            roleUserId = getPara("roleUserId");
        }
        if (StringUtil.isNotEmpty(roleKey) && StringUtil.isNotEmpty(roleUserId)) {
            where.append(" AND ").append(roleKey).append(" = ?");
            params.add(roleUserId);
        }

        //查询create_time在时间范围内
        String searchTimeList = "";
        if (isCompensate) {
            searchTimeList = body.getStr("searchTimeList");
        }else {
            searchTimeList = getPara("searchTimeList");
        }
        if (StringUtil.isNotEmpty(searchTimeList)) {
            String[] ranges = searchTimeList.split(",");
            List<String> timeConditions = new ArrayList<>();

            for (String range : ranges) {
                String[] times = range.trim().split("@");
                if (times.length == 2 &&
                        StringUtil.isNotEmpty(times[0]) &&
                        StringUtil.isNotEmpty(times[1])) {

                    timeConditions.add("(create_time >= ? AND create_time <= ?)");
                    params.add(DateUtil.parse(times[0], "yyyy-MM-dd HH:mm:ss"));
                    params.add(DateUtil.parse(times[1], "yyyy-MM-dd HH:mm:ss"));
                }
            }

            if (!timeConditions.isEmpty()) {
                where.append(" AND (");
                where.append(String.join(" OR ", timeConditions));
                where.append(")");
            }
        }
        return Map.of("where",where,"params",params);
    }


    @NotAction
    private List<Map<String,Object>> getTimeWhereAndParams(boolean isCompensate) {

        StringBuilder where = new StringBuilder(" WHERE status = 2 ");
        List<Object> params = new ArrayList<>();

        Record body = jsonBody();

        // 平仓时间筛选
        String closeTime = "";
        if (isCompensate) {
            closeTime = body.getStr("close_time");
        }else {
            closeTime = getPara("close_time");
        }
        if (StringUtil.isNotEmpty(closeTime)) {
            String closeBeginTime = closeTime.split("@")[0];
            String closeEndTime = closeTime.split("@")[1];

            if (StringUtil.isNotEmpty(closeBeginTime)) {
                where.append(" AND close_time >= ?");
                params.add(DateUtil.parse(closeBeginTime, "yyyy-MM-dd HH:mm:ss"));
            }
            if (StringUtil.isNotEmpty(closeEndTime)) {
                where.append(" AND close_time <= ?");
                params.add(DateUtil.parse(closeEndTime, "yyyy-MM-dd HH:mm:ss"));
            }
        }


        // 活动时间段 openTimeList 生成（用于判断开仓时间是否在范围内）
        Integer compensate = null;
        if (isCompensate) {
            compensate = body.getInt("compensate");
        }else {
            compensate = getParaToInt("compensate");
        }
        if (null != compensate) {
            where.append(" AND compensate = ?");
            params.add(compensate);
        }

        String roleKey = "";
        Long roleUserId = null;
        if (isCompensate) {
            roleKey = body.getStr("roleKey");
            roleUserId = body.getLong("roleUserId");
        }else {
            roleKey = getPara("roleKey");
            roleUserId = getParaToLong("roleUserId");
        }
        if (StringUtil.isNotEmpty(roleKey) && null != roleUserId) {
            where.append(" AND ").append(roleKey).append(" = ?");
            params.add(roleUserId);
        }

        //查询create_time在时间范围内
        String searchTimeList = "";
        if (isCompensate) {
            searchTimeList = body.getStr("searchTimeList");
        }else {
            searchTimeList = getPara("searchTimeList");
        }

        List<Map<String,Object>> whereList = new ArrayList<>();
        if (StringUtil.isNotEmpty(searchTimeList)) {
            String[] ranges = searchTimeList.split(",");
            for (String range : ranges) {
                List<Object> whereParams = new ArrayList<>(params);
                StringBuilder sql = new StringBuilder(where);
                String[] times = range.trim().split("@");
                if (times.length == 2 &&
                        StringUtil.isNotEmpty(times[0]) &&
                        StringUtil.isNotEmpty(times[1])) {
                    sql.append(" AND create_time >= ? AND create_time <= ? ");
                    whereParams.add(DateUtil.parse(times[0], "yyyy-MM-dd HH:mm:ss"));
                    whereParams.add(DateUtil.parse(times[1], "yyyy-MM-dd HH:mm:ss"));
                    whereList.add(Map.of("where",sql,"params",whereParams));
                }
            }
        }
        return whereList;
    }
}
